﻿using Engine.Graphics;
using Engine;

namespace Game
{
    /*
     特殊值物品的应用：
     >同一ID不同特殊值如何应用不同的模型和材质
     >特殊值功能划分位数
     >物品损坏度的说明
     >填装行为应用

     演示效果：
     编辑物品可反复切换不同特殊值的方块，
     当为指定的特殊值时，使用物品会降低耐久，
     给目标物品填装指定物品，可提高目标物品的耐久。
     翻译：
     添加新的可收缩的斧子，物品编辑可切换收缩状态
     当斧子为非收缩状态，使用斧子会掉耐久，
     给斧子填装木棍可回复指定耐久。
     */

    public class DemoBlock : Block
    {
        public const int Index = 600;

        private BlockMesh[] m_blockMeshes = new BlockMesh[2];

        private Texture2D[] m_textures = new Texture2D[2];

        public override void Initialize()
        {
            //类型0物品的模型，m_blockMeshes[0] = XXX
            Model model1 = ContentManager.Get<Model>("Models/Axe");
            Matrix boneAbsoluteTransform1 = BlockMesh.GetBoneAbsoluteTransform(model1.FindMesh("Handle").ParentBone);
            var blockMesh1 = new BlockMesh();
            blockMesh1.AppendModelMeshPart(model1.FindMesh("Handle").MeshParts[0], boneAbsoluteTransform1 * Matrix.CreateTranslation(0f, -0.5f, 0f), makeEmissive: false, flipWindingOrder: false, doubleSided: false, flipNormals: false, Color.White);
            m_blockMeshes[0] = blockMesh1;

            //类型0物品的材质，m_textures[0] = XXX
            m_textures[0] = ContentManager.Get<Texture2D>("Textures/Boat");

            //类型1物品的模型，m_blockMeshes[1] = XXX
            Model model2 = ContentManager.Get<Model>("Models/Axe");
            Matrix boneAbsoluteTransform2_1 = BlockMesh.GetBoneAbsoluteTransform(model2.FindMesh("Handle").ParentBone);
            Matrix boneAbsoluteTransform2_2 = BlockMesh.GetBoneAbsoluteTransform(model2.FindMesh("Head").ParentBone);
            var blockMesh2_1 = new BlockMesh();
            var blockMesh2_2 = new BlockMesh();
            blockMesh2_1.AppendModelMeshPart(model2.FindMesh("Handle").MeshParts[0], boneAbsoluteTransform2_1 * Matrix.CreateTranslation(0f, -0.5f, 0f), makeEmissive: false, flipWindingOrder: false, doubleSided: false, flipNormals: false, Color.White);
            blockMesh2_2.AppendModelMeshPart(model2.FindMesh("Head").MeshParts[0], boneAbsoluteTransform2_2 * Matrix.CreateTranslation(0f, -0.5f, 0f), makeEmissive: false, flipWindingOrder: false, doubleSided: false, flipNormals: false, Color.White);
            var blockMesh2_3 = new BlockMesh();
            blockMesh2_3.AppendBlockMesh(blockMesh2_1);
            blockMesh2_3.AppendBlockMesh(blockMesh2_2);
            m_blockMeshes[1] = blockMesh2_3;

            //类型1物品的材质，m_textures[1] = XXX
            m_textures[1] = ContentManager.Get<Texture2D>("Textures/Boat");
        }

        //构造地形顶点
        public override void GenerateTerrainVertices(BlockGeometryGenerator generator, TerrainGeometry geometry, int value, int x, int y, int z)
        {
        }

        //绘制非放置的物品
        public override void DrawBlock(PrimitivesRenderer3D primitivesRenderer, int value, Color color, float size, ref Matrix matrix, DrawBlockEnvironmentData environmentData)
        {
            int itemType = GetItemType(value);
            BlocksManager.DrawMeshBlock(primitivesRenderer, m_blockMeshes[itemType], m_textures[itemType], color, 2f * size, ref matrix, environmentData);
        }

        //设置物品类型，得到新的方块值value，定义物品类型为16种，取值范围为0-15，本示例只用2种，0和1
        public static int SetItemType(int value, int type)
        {
            int data = Terrain.ExtractData(value);
            int newData = (data & -16) | MathUtils.Clamp(type, 0, 15);
            int newValue = Terrain.ReplaceData(value, newData);
            return newValue;
        }

        public override bool CanWear(int value)
        {
            return base.CanWear(value);
        }

        //获取物品类型，由方块值value得到物品类型type，type取值为0-15，对应SetItemType
        public static int GetItemType(int value)
        {
            int data = Terrain.ExtractData(value);
            int type = data & 0xF;
            return type;
        }

        //设置物品受损度，受损度取值0-255
        //还需将方块表的Durability属性改为不大于255的值，Durability为损坏移除时的最大临界值
        public override int SetDamage(int value, int damage)
        {
            int num = Terrain.ExtractData(value);
            num &= -65281;
            num |= MathUtils.Clamp(damage, 0, 255) << 8;
            return Terrain.ReplaceData(value, num);
        }

        //获取物品受损度，当GetDamage(value) > Durability时物品会被移除，这是游戏自带的处理方式
        public override int GetDamage(int value)
        {
            return (Terrain.ExtractData(value) >> 8) & 0xFF;
        }
    }

    public class SubsystemDemoBlockBehavior : SubsystemBlockBehavior
    {
        public override int[] HandledBlocks => new int[] { 600 }; //当前行为方块,id600

        //使用物品时执行
        public override bool OnUse(Ray3 ray, ComponentMiner componentMiner)
        {
            int activeValue = componentMiner.ActiveBlockValue; //手持方块值
            int activeId = Terrain.ExtractContents(activeValue); //手持方块id
            if (activeId == 600)
            {
                int itemType = DemoBlock.GetItemType(activeValue); //获取物品类型
                if (itemType == 1) //如果物品类型为1
                {
                    int damage = 16; //每次使用手持方块时的损坏值
                    componentMiner.DamageActiveTool(damage); //手持方块累计损坏度加上damage，当累计损坏度>方块表Durability的值(254)时，手持方块会被移除
                    return true; //返回值true表示使用成功（使用成功玩家会有手动作，返回值一般会影响其他行为方法的执行，下同）
                }
            }
            return false; //返回值false表示使用失败
        }

        //编辑物品时执行
        public override bool OnEditInventoryItem(IInventory inventory, int slotIndex, ComponentPlayer componentPlayer)
        {
            int activeValue = componentPlayer.ComponentMiner.ActiveBlockValue; //手持方块值
            int activeId = Terrain.ExtractContents(activeValue); //手持方块id
            if (activeId == 600) //手持方块id是否为600（如果行为处理方块有多个，需要判断方块，这里其实可以不用判断）
            {
                int itemType = DemoBlock.GetItemType(activeValue); //获取物品类型
                int newItemType = itemType; //定义新的物品类型
                if (itemType == 0) newItemType = 1; //如果物品类型为0，则新物品类型改为1
                else if (itemType == 1) newItemType = 0; //如果物品类型为1，则新物品类型改为0
                int newActiveValue = DemoBlock.SetItemType(activeValue, newItemType); //得到新物品类型的方块值

                //以下为替换手持的方块，newActiveValue为要替换的新方块值
                int activeSlot = inventory.ActiveSlotIndex; //手持方块的槽位
                int activeCount = inventory.GetSlotCount(activeSlot); //手持方块的数量
                inventory.RemoveSlotItems(activeSlot, activeCount); //移除手持方块
                inventory.AddSlotItems(activeSlot, newActiveValue, activeCount); //添加新的手持方块

                return true; //返回值true表示编辑成功
            }
            return false; //返回值false表示编辑失败
        }

        //某物品被拖到当前行为方块上（Id600）时执行
        public override int GetProcessInventoryItemCapacity(IInventory inventory, int slotIndex, int value)
        {
            if (value == 23) //这里的value为被拖动的某物品的方块值，如果方块值为23，即填装的物品为木棍
            {
                int maxCapacity = 10; //行为方块的最大填装数量，这里为一次最多填装10根木棍
                return maxCapacity; //返回最大填装数量
            }
            //如果被拖动的某物品为其他方块值，则最大填装数量
            return 0; //返回0，即不会填装
        }

        //当GetProcessInventoryItemCapacity的返回值大于0时执行
        public override void ProcessInventoryItem(IInventory inventory, int slotIndex, int value, int count, int processCount, out int processedValue, out int processedCount)
        {
            int behaviorValue = inventory.GetSlotValue(slotIndex);  //当前行为方块值（这里不能获取手持方块，因为装载物品时手持方块为其他方块）
            int itemType = DemoBlock.GetItemType(behaviorValue);  //获取物品类型
            if (itemType == 0 || itemType == 1) //如果物品类型为0或1
            {
                int recovery = 10; //每一个填装的物品恢复10单位的损坏值
                Block block = BlocksManager.Blocks[600]; //获取当前行为方块的对象
                int damage = ((DemoBlock)block).GetDamage(behaviorValue); //获取当前行为方块的损坏度
                int newDamage = damage - recovery * processCount; //新的损坏度等于当前损坏度减去每个填装物品恢复的损坏值乘以填装数量，processCount为填装物品的数量
                int newBehaviorValue = ((DemoBlock)block).SetDamage(behaviorValue, newDamage);

                //以下为替换当前行为方块，newBehaviorValue为要替换的新方块值，替换的目的是更改物品的损坏度
                int slotCount = inventory.GetSlotCount(slotIndex); //当前行为方块的数量
                inventory.RemoveSlotItems(slotIndex, slotCount); //移除当前行为方块
                inventory.AddSlotItems(slotIndex, newBehaviorValue, slotCount); //添加损坏度减少的行为方块
            }
            processedValue = 0; //处理完回馈的方块值，为0表示不回馈
            processedCount = 0; //处理完回馈的方块数量
        }
    }
}